﻿using Menu.Exceptions;
using Menu.Renderers;
using MenuService.Interfaces;
using MenuService.Loaders;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MenuService.Public
{
    public class MenuControlFacade
    {
        private IMenuProvider                    _menuProvider;
        private Dictionary<ConsoleKey, MenuItem> _boundKeys;
        private Dictionary<MenuItem, Action>     _commandHandler;
        private Frame                            _selectionFrame;
        private int                              _selectedItemIndex;
        private MenuItem                         _selectedItem;
        private Menu _menu;
        private MenuItem                         _currentMenuLevel;
        private int                              _menuItemsCount;

        private Menu CurrentMenu
        {
            get
            {
                if (_menu == null)
                    _menu = _menuProvider.LoadMenu();

                return _menu;
            }
        }

        public MenuControlFacade(String fileName)
        {
            _menuProvider   = new TxtFileMenuLoader(fileName);
            _boundKeys      = new Dictionary<ConsoleKey, MenuItem>();
            _commandHandler = new Dictionary<MenuItem, Action>();
            _selectionFrame = new Frame();

            _selectedItemIndex = 0;
        }

        public void PrintMenu2Console()
        {
            Print(null);
        }

        public void BindMethod(String commandKey,Action action)
        {
            MenuItem item = this.CurrentMenu.FindItemByKey(commandKey);

            if (item == null)
                throw new MenuServiceException(MenuLogicFaults.ItemIsNotFound);

            _commandHandler.Add(item,action);
        }

        internal void Print(MenuItem parentItem)
        {
            _currentMenuLevel = parentItem;

            Console.Clear();

            IEnumerable<MenuItem> itemsSource = null;

            if (parentItem == null)
            {
                itemsSource = this.CurrentMenu.Items;
            }
            else
            {
                itemsSource =  parentItem.ChildItems;
            }
            
            Console.CursorVisible = false;

            _boundKeys.Clear();
            ConsoleKey startKey = ConsoleKey.A;

            bool isFirstItem = true;
            _menuItemsCount  = 0;

            foreach (MenuItem mi in itemsSource)
            {
                if (isFirstItem)
                    Console.WriteLine();

                String displayText = String.Format("  {0}. {1}", startKey, mi.ItemText);
                Console.WriteLine(displayText);
                Console.WriteLine();
                _boundKeys.Add(startKey, mi);
                startKey++;

                isFirstItem = false;

                if (_menuItemsCount == _selectedItemIndex)
                {
                    _selectionFrame.SetFrame(0,
                        _selectedItemIndex * 2,
                        displayText.Length + 1,
                        _selectedItemIndex * 2 + 2);

                    _selectedItem = itemsSource.ElementAt(_selectedItemIndex);
                    Console.SetCursorPosition(0, _selectedItemIndex * 2 + 3);
                }

                _menuItemsCount++;
            }

            if (parentItem != null)
            {
                if (isFirstItem)
                    Console.WriteLine();

                String displayText = String.Format("  {0}. << Назад", startKey);
                Console.WriteLine(displayText);
                
                _boundKeys.Add(startKey, parentItem.ParentItem);

                if (_menuItemsCount == _selectedItemIndex)
                {
                    _selectionFrame.SetFrame(0,
                        _selectedItemIndex * 2,
                        displayText.Length + 1,
                        _selectedItemIndex * 2 + 2);

                    _selectedItem = parentItem.ParentItem;
                    Console.SetCursorPosition(0, _selectedItemIndex * 2 + 3);
                }

                _menuItemsCount++;
            }
        }

        private void ExecMenuItem(MenuItem menuItem)
        {
            if (menuItem != null &&_commandHandler.ContainsKey(menuItem))
            {
                Action a = _commandHandler[menuItem];
                Console.Clear();
                a();

                Print(menuItem.ParentItem ?? menuItem);
            }
            else
            {
                _selectedItemIndex = 0;
                Print(menuItem);
            }
        }

        public void StartInputListening()
        {
            for (;;)
            {
                ConsoleKeyInfo ki = Console.ReadKey(true);
                if (_boundKeys.ContainsKey(ki.Key))
                {
                    MenuItem boundItem = _boundKeys[ki.Key];

                    if (boundItem == null)
                    {
                        Print(null);
                    }
                    else
                    {
                        ExecMenuItem(boundItem);
                    }
                }
                else if (ki.Key == ConsoleKey.DownArrow)
                {
                    _selectedItemIndex++;
                    if (_selectedItemIndex >= _menuItemsCount)
                        _selectedItemIndex = 0;

                    Print(_currentMenuLevel);
                }
                else if (ki.Key == ConsoleKey.UpArrow)
                {
                    _selectedItemIndex--;
                    if (_selectedItemIndex < 0)
                        _selectedItemIndex = _menuItemsCount - 1;

                    Print(_currentMenuLevel);
                }
                else if (ki.Key == ConsoleKey.Enter)
                {
                    ExecMenuItem(_selectedItem);
                }
            }
        }
    }
}
